package com.mumu.muPictureBackend.aop;

import com.mumu.muPictureBackend.annotation.AuthCheck;
import com.mumu.muPictureBackend.constant.UserConstant;
import com.mumu.muPictureBackend.exception.BusinessException;
import com.mumu.muPictureBackend.exception.ErrorCode;
import com.mumu.muPictureBackend.exception.ThrowUtils;
import com.mumu.muPictureBackend.model.entity.User;
import com.mumu.muPictureBackend.model.enums.UserRoleEnum;
import com.mumu.muPictureBackend.model.vo.UserVO;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Aspect//表示这是一个AOP切面
@Component//表示这是一个springBean
@Slf4j
public class AuthInterceptor {
    /**
     * @param joinPoint
     * @param authCheck
     * @return
     * @throws Throwable
     * @Aroud("@annotation(authCheck)") 只要是注上了annotation(authCheck)注解的方法 都会在这里被拦截 Aroud是环形拦截 是该请求在调用之前来执行下面的方法 请求执行完毕之后也会调用该方法
     * 下面的方法就会判断该请求是可以用什么权限进行访问 比如是ADMIN还是USER 还是VIP权限
     * 像登录 访问首页这些匿名用户就不需要该注解 直接同行 像具体的方法 比如可以操作用户账号等就需要该注解标注权限了 也就是注上该注解的方法至少是USER登录之后才能访问的
     */
    @Around("@annotation(authCheck)")
    public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable {
        //获取该方法上的注解中的权限 比如ADMIN,USER
        UserRoleEnum mustRole = authCheck.mustRole();
        //然后获取session中的用户对象 需要看该对象是USER权限还是ADMIN权限
        //RequestContextHolder是spring提供的一个工具类 用于在非Web层(比如AOP)中获取当前请求的上下文 它把请求中的HttpServletRequest放到了ThreadLocal当中 就可以这样跨层访问了
        RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
        //这里获取到request中的Session然后再获取里面登录的user对象 看看里面的权限
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
        Object userObj = request.getSession().getAttribute(UserConstant.USER_LOGIN_STATE);
        //如果userObj为空 那么就表示都没有登录 那么不给访问
        ThrowUtils.throwIf(userObj == null, ErrorCode.NOT_LOGIN_ERROR, "先去登录再来访问好不好");
        //然后如果登录了 那么就看看该方法是不是USER权限就可以访问的 如果是的 那么直接给访问
        User user = (User) userObj;
        //获取用户的权限枚举
        UserRoleEnum userRoleEnum = UserRoleEnum.getUserRoleEnum(user.getUserRole());
        //如果指定的权限在UserRoleEnum中枚举的权限中没有 那么就抛异常
        ThrowUtils.throwIf(mustRole == null, ErrorCode.FORBIDDEN_ERROR);
        //用户的权限值大于等于请求的权限值那么就给通行 0-user 1-vip 2-admin
        if (userRoleEnum.getValue() >= mustRole.getValue()) {
            log.info("user的权限={},请求的权限={} 放行", userRoleEnum.getText(), mustRole.getText());
            return joinPoint.proceed();
        }

        log.warn("权限验证失败: 用户角色={}({}), 所需角色={}({})",
                userRoleEnum.getText(), userRoleEnum.getValue(),
                mustRole.getText(), mustRole.getValue());
        throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
    }
}
